package com.irisking.server;

import com.irisking.server.callback.IDispatcher;
import com.irisking.server.client.ClientManager;
import com.irisking.server.client.DeviceClient;
import com.irisking.server.coder.RequestDecoder;
import com.irisking.server.coder.ResponseEncoder;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;

import java.util.ArrayList;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * netty服务端入门
 * 
 * @author -weiz-
 * 
 */
public class NettyServer {

	private IDispatcher dispatcherCallBack;
	private int port ;

	private EventLoopGroup bossGroup;
	private EventLoopGroup workerGroup;
	private ServerBootstrap bootstrap;


	private Timer timerBeat;
	private List<String> offlineDevice = new ArrayList<>();

	public NettyServer(int port, IDispatcher dispatcherCallBack){
		this.dispatcherCallBack = dispatcherCallBack;
		this.port = port;
	}

	private void initBeatTimer(){
		// 创建定时器任务
		TimerTask timerTask = new TimerTask() {
			@Override
			public void run() {
				System.out.println("开始处理心跳，当前设备连接数：" + ClientManager.getAll().size());
				for (String key : ClientManager.getAll()){
					//处理心跳
					DeviceClient client = ClientManager.get(key);
					int beat = client.getBeatCount();
					if (client.getBeatCount() > 6)
					{
						// 心跳大于6 ，加入到待删除列表;
						offlineDevice.add(key);
					}else{
						client.setBeatCount(beat++);
						ClientManager.remove(key);
						ClientManager.put(key,client);
					}
				}
				// 删除离线设备
				for (String key : offlineDevice){
					System.out.print("开始处理断线：" + key);
					DeviceClient client = ClientManager.get(key);
					if(client.isConnected()){
						client.close();
					}
					ClientManager.remove(key);
				}
			}
		};

		// 创建定时器
		timerBeat = new Timer();
		timerBeat.scheduleAtFixedRate(timerTask, 5, 20000);
	}

	/**
	 * 启动Netty服务
	 */
	public void startServer(){
		ExecutorService executorService = Executors.newSingleThreadExecutor();
		executorService.submit(()->{
			System.out.println("启动AI后台服务端，端口 " + this.port);
			try{
				// 启动心跳检测
				initBeatTimer();
				// 启动后台服务端
				initNettyServer(this.port);
			}catch (Exception e){
				e.printStackTrace();
			}
			System.out.println("启动AI后台服务运行结束");
		});
	}

	/**
	 * 启动
	 */
	private void initNettyServer(int port) {
		// 创建boss和worker
		bossGroup = new NioEventLoopGroup();
		workerGroup = new NioEventLoopGroup();
		// 服务类
		bootstrap = new ServerBootstrap();
		try {
			// 设置循环线程组事例
			bootstrap.group(bossGroup, workerGroup);
			// 设置channel工厂
			bootstrap.channel(NioServerSocketChannel.class);
			// 设置管道
			bootstrap.childHandler(new ChannelInitializer<SocketChannel>() {
				@Override
				public void initChannel(SocketChannel ch) throws Exception {
					ch.pipeline().addLast(new RequestDecoder());
					ch.pipeline().addLast(new ResponseEncoder());
					ch.pipeline().addLast(new NettyServerHandler(dispatcherCallBack));
				}
			});

			// 链接缓冲池队列大小
			bootstrap.option(ChannelOption.SO_BACKLOG, 2048)
			.option(ChannelOption.SO_SNDBUF, 16*1024)
			.option(ChannelOption.SO_RCVBUF, 16*1024);

			ChannelFuture future = doAccept(port);
			System.out.println("start!!!");
			// 绑定端口
			future.channel().closeFuture().sync();
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			this.bossGroup.shutdownGracefully();
			this.workerGroup.shutdownGracefully();
		}
	}

	/**
	 *
	 * @param port
	 * @return
	 * @throws InterruptedException
	 */
	public ChannelFuture doAccept(int port) throws InterruptedException {
		bootstrap.childHandler(new ChannelInitializer<SocketChannel>() {
			@Override
			protected void initChannel(SocketChannel ch) throws Exception {
				ch.pipeline().addLast(new RequestDecoder());
				ch.pipeline().addLast(new ResponseEncoder());
				ch.pipeline().addLast(new NettyServerHandler(dispatcherCallBack));
			}
		});
		ChannelFuture future = bootstrap.bind(port).sync();
		return future;
	}

	/**
	 * 发送Ack 消息到客户度
	 * @param key
	 * @param json
	 */
	public void sendAckData(String key, String json) {
		if (key==null) {
			return;
		}
		ClientManager.sendAckData(key,json);
	}

	/**
	 * 发送消息到客户度
	 * @param key
	 * @param json
	 */
	public void sendCommandData(String key, String json) {
		if (key==null) {
			return;
		}
		ClientManager.sendCommandData(key,json);
	}
}
